package edu.gmu.atelier;

import java.util.ArrayList;
import java.util.List;

/**
 * Vector defined within some vector space.
 * @author James H. Pope
 */
public class ComplexVector implements Vector
{
    private ComplexElement[] elements = null;
    
    public ComplexVector(ComplexElement[] comps)
    {
        this.elements = comps;
    }
    
    //------------------------------------------------------------------------//
    // Interface methods
    //------------------------------------------------------------------------//
    public ComplexVector normalize()
    {
        double n = 0;
        for (int i = 0; i < elements.length; i++)
        {
            ComplexElement z = elements[i];
            //double r = z.norm().getReal(); //no reason to create interm object
            double r = z.a * z.a + z.b * z.b;
            //System.out.println("r: "+i+"="+r);
            n += r;
        }
        double sqrtN = Math.sqrt(n);
        //System.out.println("Average: "+avgMag);
        
        ComplexElement[] elems = new ComplexElement[this.elements.length];
        for (int i = 0; i < elems.length; i++)
        {
            ComplexElement elem = this.elements[i];
            elems[i] = new ComplexElement( elem.a/sqrtN, elem.b/sqrtN );
        }
        return new ComplexVector(elems);
    }
    
    public ComplexElement norm2()
    {
        /*
         * Has to be dot product with element and it's complex conjugate
         */
        double norm = 0;
        for (int i = 0; i < elements.length; i++)
        {
            ComplexElement z = elements[i];
            ComplexElement z_barz = z.conjugate().mult( z );
            //System.out.println("norm2 r: "+i+"="+r);
            norm += z_barz.abs().getReal();
        }
        return new ComplexElement(norm, 0.0);
    }
    
    public ComplexVector add(Vector v)
    {
        ComplexElement[] elems = new ComplexElement[this.elements.length];
        for (int i = 0; i < elements.length; i++)
        {
            elems[i] = elements[i].add( v.getElement(i) );
        }
        return new ComplexVector(elems);
    }
    
    public ComplexVector sub(Vector v)
    {
        ComplexElement[] elems = new ComplexElement[this.elements.length];
        for (int i = 0; i < elements.length; i++)
        {
            //elems[i] = elements[i].add( v.getElement(i).negate() );
            elems[i] = elements[i].sub( v.getElement(i) );
        }
        return new ComplexVector(elems);
    }
    
    public Vector mult(Element e)
    {
        ComplexElement[] elems = new ComplexElement[this.elements.length];
        for (int i = 0; i < elements.length; i++)
        {
            elems[i] = elements[i].mult( e );
        }
        return new ComplexVector(elems);
    }
    
    public ComplexElement dot(Vector v )
    {
        /*
         * http://college.cengage.com/mathematics/larson/elementary_linear
         *                                        4e/shared/downloads/c08s4.pdf 
         * 
         * The dot product of complex numbers is u.v = u^T*v_conj.
         * 
         * This agrees with Olver-Shakiban page 175 Complex Vector Spaces.
         * This is known as the "Hermitian dot product on C^n". It is not
         * symmetric z.w != w.z, however, z.w = conj(w.z), i.e. conjugate sym
         * 
         * Note: Dr. Strang has nicer explanation, when you transpose
         * complex vectors always take their conjugate (hermitian).
         *   e.g. z.w = z^H*w = z^T_conj*w
         * We use Dr. Strang's method throughout.
         */
        // @todo MAKE MORE EFFICIENT, DO NOT NEED INTERMEDIATE CONJUGATE ELEMS!
        ComplexVector z = this;
        ComplexVector w = (ComplexVector)v;
        ComplexElement dot = z.elements[0].conjugate().mult( w.elements[0] );
        for (int i = 1; i < elements.length; i++)
        {
            dot.multAddTo(elements[i].conjugate(), w.elements[i]);
        }
        return dot;
    }
    
    public ComplexElement getElement(int i)
    {
        return this.elements[i];
    }
    
    public void setElement(int i, Element e)
    {
        this.elements[i] = (ComplexElement)e;
    }
    
//    public Element[] getElements()
//    {
//        return this.elements;
//    }
    
    public int getLength()
    {
        return this.elements.length;
    }
    
    public ComplexVector mult( IMatrix a )
    {
        java.util.List<Vector> vectors = a.getVectors();
        ComplexElement[] x = this.elements;
        ComplexElement[] b = new ComplexElement[a.getRows()];
        for (int j = 0; j < b.length; j++)
        {
            b[j] = new ComplexElement(0.0,0.0);
        }
        
        for (int i = 0; i < x.length; i++)
        {
            ComplexVector v = (ComplexVector)vectors.get(i);
            ComplexElement[] vi = v.elements;
            ComplexElement xWeight = x[i];
            for (int j = 0; j < vi.length; j++)
            {
                // NOTE: Repeated from ComplexNumber for performance reasons
                ComplexElement c2 = vi[j];
                double ta = (xWeight.a * c2.a) - (xWeight.b * c2.b);
                double tb = (xWeight.b * c2.a) + (xWeight.a * c2.b);

                b[j].a += ta;
                b[j].b += tb;
            }
            
        }
        return new ComplexVector(b);
    }
    
    /**
     * Returns true is all the elements are the zero element.
     * @param
     */
    public boolean isZero()
    {
        for (int i = 0; i < elements.length; i++)
        {
            if( elements[i].isZero() == false )
            {
                return false;
            }
        }
        return true;
    }
    
    public ComplexVector copy()
    {
        ComplexElement[] elems = new ComplexElement[this.elements.length];
        for (int i = 0; i < elems.length; i++)
        {
            elems[i] = this.elements[i].copy();
        }
        return new ComplexVector(elems);
    }
    
    public ComplexVector copy(int start, int length)
    {
        ComplexElement[] elems = new ComplexElement[length];
        for (int i = 0; i < elems.length; i++)
        {
            elems[i] = this.elements[start+i].copy();
        }
        return new ComplexVector(elems);
    }
    
    public void copyElements(int start, Vector v)
    {
        ComplexVector cv = (ComplexVector)v;
        for (int r = 0; r < cv.elements.length; r++)
        {
            elements[start+r] = cv.elements[r];
        }
    }
    
    public Matrix transpose( Matrix a )
    {
        List<Vector> vectors = a.getVectors();
        List t = new ArrayList<Vector>();
        int n = vectors.size();
        int m = vectors.get(0).getLength();
        
        /*
         * ComplexVector transposes require that they be conjugated
         */
        for (int i = 0; i < m; i++)
        {
            ComplexElement[] c = new ComplexElement[n];
            for (int j = 0; j < n; j++)
            {
                ComplexElement z = (ComplexElement)vectors.get(j).getElement(i);
                c[j] = z.conjugate();
            }
            t.add( new ComplexVector(c) );
        }
        return new Matrix(t, a.getFactory() );
    }
    
    //------------------------------------------------------------------------//
    // Vector specific "row" mutator operations
    //------------------------------------------------------------------------//
    public void swap( int fromRow, int toRow )
    {
        ComplexElement temp = this.elements[fromRow];
        this.elements[fromRow] = this.elements[toRow];
        this.elements[toRow] = temp;
    }
    
    public void mulAddTo( int fromRow, Element mult, int toRow )
    {
        this.elements[toRow].multAddTo(mult, this.elements[fromRow]);
    }
    
    public void mulTo( int row, Element mult )
    {
        this.elements[row].multTo(mult);
    }
    
    public Vector extract( int[] B )
    {
        ComplexElement[] es = new ComplexElement[B.length];
        for (int i = 0; i < es.length; i++)
        {
            es[i] = this.elements[B[i]];
        }
        return new ComplexVector(es);
    }
    
    public void extractTo( int[] B, Vector b )
    {
        ComplexVector v = (ComplexVector)b;
        for (int i = 0; i < B.length; i++)
        {
            v.elements[i] = this.elements[B[i]];
        }
    }
    
    //------------------------------------------------------------------------//
    // Class instance methods
    //------------------------------------------------------------------------//
    public boolean similar( Object obj, Epsilon e )
    {
        if(obj == this)
        {
            return true;
        }
        
        if( (obj instanceof ComplexVector) == false )
        {
            return false;
        }
        
        ComplexVector o = (ComplexVector)obj;
        
        if( o.elements.length != this.elements.length )
        {
            return false;
        }
        
        if( e != null )
        {
            for (int i = 0; i < this.elements.length; i++)
            {
                // Need epsilon check here
                //if( this.components[i] != o.components[i] )
                if( this.elements[i].similar(o.elements[i], e) == false )
                {
                    return false;
                }
            }
        }
        else
        {
            for (int i = 0; i < this.elements.length; i++)
            {
                // Exact check, no epsilon specified
                if( this.elements[i] == o.elements[i] )
                {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    public boolean equals( Object obj )
    {
        return this.similar(obj, null);
    }
    
    public String toString()
    {
        StringBuilder buf = new StringBuilder("[");
        for (int i = 0; i < this.elements.length; i++)
        {
            buf.append( this.elements[i] );
            buf.append( "," );
        }
        buf.append("]");
        return buf.toString();
    }
    
    /*
     * Some interesting stuff from MathFram - Google: Normalizing Complex Vectors
     */
    //On Wed, 20 Nov 2002, Doug Magnoli wrote:
    //
    //> Since it's analogous both to the normalization of complex numbers and to
    //> the normalization of 3-D vectors, it certainly looks right to me. If
    //> it's right, you can dot the vector with itself, and you should get 1, and
    //> it looks like that's what you'll get.
    //>
    //Dot the vector with it's conjugate. Otherwise
    //(i,i)*(i,i) = -2 with 'length' i sqr 2, while
    //(i,i)*(-i,-i) = 2 with a real length of sqr 2
    //for vectors in C^2
    //
    //Re: Normalizing Complex Vectors 
    //Posted: Nov 20, 2002 6:54 PM	
    // 		Plain Text	 		Reply
    //
    //
    //Since it's analogous both to the normalization of complex numbers and to
    //the normalization of 3-D vectors, it certainly looks right to me. If
    //it's right, you can dot the vector with itself, and you should get 1, and
    //it looks like that's what you'll get.
    //
    //-Doug Magnoli
    //[Delete the two and the three for email.]
    //
    //
    //
    //
    //David wrote:
    //
    //> Hello.
    //>
    //> I would like to confirm that I am normalizing complex vectors
    //> properly.
    //> Is there an "proper" way to normalize a complex vector?
    //>
    //> Following is what I am doing presently.
    //>
    //> Say each vector component is made up of the real and imaginary
    //> component u and v respectively:
    //> v-component, z = u + v i ("i" denotes the imaginary component).
    //>
    //> Presently, I add the square of all components of a particular vector,
    //> take the square root of this total to determine the norm2, then divide
    //> all components by this norm2.
    //>
    //> Here is an example, for a 3-component vector, v:
    //>
    //> v =
    //>
    //> u1 + v1 i (first component)
    //> u2 + v2 i (second component)
    //> u3 + v3 i (third component)
    //>
    //> Square these components:
    //> square of norm2, sqr(N) = (u1)*(u1) + (v1)*(v1) + (u2)*(u2) + (v2)*(v2)
    //> + (u3)*(u3) + (v3)*(v3)
    //>
    //> Then the Norm = N.
    //>
    //> The normalized complex vector is now:
    //>
    //> v-normalized =
    //>
    //> (u1/N) + (v1/N) i (first component)
    //> (u2/N) + (v2/N) i (second component)
    //> (u3/N) + (v3/N) i (third component)
    //>
    //> Is this the correct way to normalize a complex vector?
    //>
    //> Thanks.
}
